package com.ccll.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccll.constants.GlobalConstant;
import com.ccll.domain.common.BaseContextHolder;
import com.ccll.domain.dto.PageDto;
import com.ccll.domain.http.Result;
import com.ccll.domain.po.File;
import com.ccll.mapper.FileMapper;
import com.ccll.service.FileService;
import com.ccll.utils.PageHelperUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Seon
 * @date 2022/11/12 20:46
 */
@Slf4j
@RestController
@RequestMapping("/file")
public class FileController {
    @Autowired
    private FileService fileService;

    @Autowired
    private FileMapper fileMapper;

    /**
     * 查看文件列表（普通用户）
     *
     * @param map 传入参数，其中只有一个键initPath
     * @return
     */
    @PostMapping("/getFilePageUser")
    public Result<?> getAllFileUser(@RequestBody Map<String, Object> map) {
        @NotNull String initPath = (String) map.get("initPath");
        if (!initPath.endsWith("/")) {
            initPath += "/";
        }
        List<File> userFolder = fileService.getUserFolder(initPath);
        log.info("用户id = {} 获取了文件列表，initPath = {}", BaseContextHolder.getCurrentId(), initPath);
        return Result.success(userFolder);
    }

    /**
     * 获取详细文件或文件夹详细信息
     */
    @GetMapping("/getFileInformation")
    public Result<?> getFileInformation(@RequestParam("id") Long fileId) {
        File file = fileService.getById(fileId);
        if (file == null) {
            log.error("文件id = {}, 此文件不存在，请检查服务器", fileId);
            return Result.error("文件不存在", 0);
        }
        if (0 == file.getIsFolder()) {
            return Result.success(file);
        } else {
            Map<String, Object> folderInformation = fileService.getFolderInformation(fileId);
            return Result.success(folderInformation);
        }
    }

    /**
     * 添加文件夹
     *
     * @param map 传递参数的map，里面有两个键(initPath, addPath)
     */
    @PostMapping("/addFolder")
    public Result<?> addFolder(@RequestBody Map<String, Object> map) {
        Long userId = BaseContextHolder.getCurrentId();
        String initPath = (String) map.get("initPath");
        String addPath = (String) map.get("addPath");

        if (!initPath.endsWith("/")) {
            initPath += "/";
        }
        if (addPath.startsWith("/")) {
            addPath = addPath.substring(addPath.indexOf("/") + 1);
        }
        String newUserPath = initPath + addPath;
        /*
        如果是文件夹的话，那第一次就直接换成大小为零。然后后面每次前端掉的时候都传递实际大小。
         */
        File file = File.builder()
                .userId(userId)
                .fileName(addPath)
                .filePath(newUserPath)
                .fileSize(BigDecimal.ZERO)
                .isFolder(1)
                .build();
        try {
            fileService.save(file);
            return Result.success("添加文件夹成功");
        } catch (Exception e) {
            log.error("由于文件夹重名，添加文件夹失败, userId = {}, addPath = {}",
                    BaseContextHolder.getCurrentId(), addPath);
            return Result.error("由于文件夹重名，添加文件夹失败", 0);
        }
    }

    /**
     * 文件分类查询
     *
     * @param pageDto
     * @param type
     * @return
     */
    @GetMapping("/getFilePageByType")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> getFilePageByType(PageDto pageDto, @RequestParam("type") String type) {
        if (!Arrays.asList(GlobalConstant.fileType).contains(type)) {
            return Result.error("没有这种类型哦", 0);
        }
        Page<File> fileByType = fileService.getFileByType(pageDto, type);
        if (fileByType.getRecords().size() == 0) {
            log.error("没有{}类型的文件", type);
            return Result.error("暂无该类型的文件哦", 0);
        } else {
            return Result.success(fileByType);
        }
    }

    /**
     * 用户通过文件名搜索文件
     */
    @PostMapping("/userSearch")
    public Result<?> userSearchFile(@RequestBody Map<String, Object> map) {
        @NotNull String filename = (String) map.get("filename");
        if (!StringUtils.hasText(filename)) {
            return Result.error("查询文件名为空", 0);
        }
        Long userId = BaseContextHolder.getCurrentId();
        List<File> files = fileService.searchFileCondition(userId.toString(), filename, null);
        return Result.success(files);
    }

    /**
     * 管理员通过文件名搜索文件，可以再加上一个type来限制搜索类型。
     */
    @PostMapping("/adminSearch")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> adminSearchFile(@RequestBody Map<String, Object> map) {
        @NotNull String filename = (String) map.get("filename");
        String type = (String) map.get("type");
        if (!StringUtils.hasText(filename)) {
            return Result.error("查询文件名为空", 0);
        }
        if (!Arrays.asList(GlobalConstant.fileType).contains(type)) {
            log.warn("接收到恶意请求（无此文件类型）, fileType = {}", type);
            return Result.error("文件类型错误，请不要绕过前端请求后端哦！", 0);
        }
        List<File> files = fileService.searchFileCondition(null, filename, type);
        return Result.success(files);
    }

    /**
     * 删除文件夹
     *
     * @param map 其中的参数键值为{folderName}
     * @return
     */
    @PostMapping("/deleteFolder")
    public Result<?> deleteFolder(@RequestBody Map<String, Object> map) {
        String folderName = (String) map.get("folderName");
        if (!StringUtils.hasText(folderName)) {
            return Result.error("文件夹名为空", 0);
        }
        if (!folderName.startsWith("/")) {
            folderName = "/" + folderName;
        }
        boolean delete = fileService.deleteFolder(folderName);
        if (delete) {
            return Result.success("删除成功");
        } else {
            return Result.error("删除失败", 0);
        }
    }

    /**
     * 删除文件
     *
     * @param ids
     * @return
     */
    @DeleteMapping("/deleteFile")
    public Result<?> deleteFile(@RequestParam("ids") List<Long> ids) {
        boolean remove = fileService.removeByIds(ids);
        if (remove) {
            return Result.success("删除成功");
        } else {
            return Result.error("删除失败", 0);
        }
    }

    /**
     * 删除回收站文件
     *
     * @param ids
     * @return
     */
    @DeleteMapping("/deleteRecycle")
    public Result<?> deleteRecycle(@RequestParam("ids") List<Long> ids) {
        try {
            ids.forEach(id -> {
                File judgeFile = fileMapper.physicalSearch(id);
                String judgePath = judgeFile.getRealPath();
                boolean isReference = fileService.isRealPathReference(judgePath);
                if (!isReference) {
                    //要从本地删掉文件
                    java.io.File file = new java.io.File(judgePath);
                    boolean delete = file.delete();
                    if (!delete) {
                        log.warn("服务器本地文件疑似丢失，请检查,realPath = {}", judgePath);
                    }
                }
                fileMapper.physicalDelete(id);
            });
            return Result.success("删除成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("删除失败", 0);
        }
    }

    @DeleteMapping("/adminDeleteFile")
    @PreAuthorize("hasAnyAuthority('system:admin')")
    public Result<?> adminDeleteFile(@RequestParam("ids") List<Long> ids) {
        try {
            ids.forEach(id -> {
                File deleteFile = fileMapper.physicalSearch(id);
                String deletePath = deleteFile.getRealPath();
                //要从本地删掉文件
                java.io.File file = new java.io.File(deletePath);
                boolean delete = file.delete();
                if (!delete) {
                    log.warn("服务器本地文件疑似丢失，请检查,realPath = {}", deletePath);
                }
                fileMapper.physicalDelete(id);
            });
            return Result.success("删除成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("删除失败", 0);
        }
    }

    /**
     * 恢复回收站中的文件
     *
     * @param ids
     * @return
     */
    @PutMapping("/recovery")
    public Result<?> recovery(@RequestParam("ids") List<Long> ids) {
        try {
            ids.forEach(id -> {
                fileService.recoveryFile(id);
            });
            return Result.success("恢复成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.error("恢复失败", 0);
        }
    }

    /**
     * 复制文件或者文件夹
     *
     * @param map
     * @return
     */
    @PostMapping("/copy")
    public Result<?> copy(@RequestBody Map<String, Object> map) {
        String originPath = (String) map.get("originPath");
        String targetPath = (String) map.get("targetPath");
        if (originPath.endsWith("/")) {
            originPath = originPath.substring(0, originPath.lastIndexOf("/"));
        }
        if (targetPath.endsWith("/")) {
            targetPath = targetPath.substring(0, targetPath.lastIndexOf("/"));
        }
        try {
            fileService.copy(originPath, targetPath);
            return Result.success("复制成功");
        } catch (Exception e) {
            return Result.error("目标文件夹中存在同名文件或文件夹，复制失败", 0);
        }
    }

    /**
     * 移动文件或者文件夹
     *
     * @param map
     * @return
     */
    @PutMapping("/move")
    public Result<?> move(@RequestBody Map<String, Object> map) {
        String originPath = (String) map.get("originPath");
        String targetPath = (String) map.get("targetPath");
        if (originPath.endsWith("/")) {
            originPath = originPath.substring(0, originPath.lastIndexOf("/"));
        }
        if (targetPath.endsWith("/")) {
            targetPath = targetPath.substring(0, targetPath.lastIndexOf("/"));
        }

        try {
            fileService.copy(originPath, targetPath);
            Long userId = BaseContextHolder.getCurrentId();
            LambdaQueryWrapper<File> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(File::getUserId, userId);
            wrapper.likeRight(File::getFilePath, originPath);
            List<File> willDeleteFile = fileService.list(wrapper);
            for (File file : willDeleteFile) {
                Long id = file.getId();
                fileMapper.physicalDelete(id);
            }
            return Result.success("移动成功");
        } catch (Exception e) {
            return Result.error("目标文件夹中存在同名文件或文件夹，复制失败", 0);
        }
    }

    /**
     * 获取回收站的分页
     *
     * @param pageDto
     * @return
     */
    @GetMapping("/getRecyclePage")
    public Result<?> getRecyclePage(PageDto pageDto) {
        Integer currentPage = pageDto.getCurrentPage();
        Integer pageSize = pageDto.getPageSize();
        Map<String, Object> map = PageHelperUtil.getPageData(pageDto);

        map.put("userId", BaseContextHolder.getCurrentId());
        List<File> recyclePage = fileMapper.getRecyclePage(map);
        Long total = fileMapper.countRecycle(map);
        long pages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;

        Page<File> page = new Page<>();
        page.setCurrent(currentPage);
        page.setSize(pageSize);
        page.setPages(pages);
        page.setTotal(total);
        page.setRecords(recyclePage);

        return Result.success(page);
    }

    /**
     * 重命名文件夹或文件
     *
     * @param map 里面的键值对，键为{originName, targetName}
     * @return
     */
    @PutMapping("/rename")
    public Result<?> rename(@RequestBody Map<String, Object> map) {
        String originName = (String) map.get("originName");
        String targetName = (String) map.get("targetName");
        if (originName.endsWith("/")) {
            originName = originName.substring(0, originName.lastIndexOf("/"));
        }
        if (targetName.endsWith("/")) {
            targetName = targetName.substring(0, targetName.lastIndexOf("/"));
        }
        try {
            fileService.rename(originName, targetName);
            return Result.success("重命名成功");
        } catch (Exception e) {
            return Result.error("由于目标文件夹或文件已经存在，重命名失败", 0);
        }
    }
}
